#include "PacketListener.h"
#include<iostream>
#include<net/ethernet.h>
#include<pcap.h>
#include<boost/thread/thread.hpp>
#include<boost/bind.hpp>
#include<unistd.h>

using namespace std;
using namespace boost;

PacketListener::PacketListener(string* destination, string* device, string* filter)
{
	this -> destinationIP = destination;
	this -> device = device;
	this -> stopFlag = false;
	this -> filter = filter;
	this -> err = new char[PCAP_ERRBUF_SIZE];
	this -> fProg = (bpf_program*)malloc(sizeof(bpf_program));
	this -> header = (pcap_pkthdr*)malloc(sizeof(pcap_pkthdr));
	this -> net = (bpf_u_int32*)malloc(sizeof(bpf_u_int32));
	this -> mask = (bpf_u_int32*)malloc(sizeof(bpf_u_int32));
	if ((pcap_lookupnet(device->c_str(),
				this->net,
				this->mask,
				this->err)) == -1)
	{
		cout << "Error looking up net mask: " << this->err << endl;
		exit(1);
	}
	if ((this->pcapHandle = pcap_open_live(this->device->c_str(),
						BUFSIZ,
						0,
						1000,
						this->err)) == NULL)
	{
		cout << "Error opening device for capture: " << this->err << endl;
		exit(1);
	}
	if ((pcap_setnonblock(this->pcapHandle,
				0,
				this->err)) == -1)
	{
		cout << "Error setting nonblock: " << this->err << endl;
		exit(1);
	}
	if ((pcap_compile(this->pcapHandle,
				this->fProg,
				this->filter->c_str(),
				0,
				*this->mask)) == -1)
	{
		cout << "Error compiling filter: " << pcap_geterr(this->pcapHandle) << endl;
		exit(1);
	}
	if ((pcap_setfilter(this->pcapHandle,
				this->fProg)) == -1)
	{
		cout << "Error setting filter: " << pcap_geterr(this->pcapHandle) << endl;
		exit(1);
	}
	switch(pcap_datalink(this->pcapHandle)) {
	case DLT_EN10MB:
		this -> offset = 14;
		break;
	case DLT_IEEE802:
		this -> offset = 22;
		break;
	case DLT_FDDI:
		this -> offset = 21;
		break;
	case DLT_NULL:
		this -> offset = 4;
		break;
	case DLT_RAW:
		this -> offset = 0;
		break;
	default:
		fprintf(stderr, "\n%s bad datalink type", device);
		exit (EXIT_FAILURE);
		break;
	}
	this -> lt = new thread(bind(&PacketListener::run,this));
}

PacketListener::~PacketListener()
{
	pcap_freecode(this->fProg);
	pcap_close(this->pcapHandle);
	delete this -> err;
	delete this -> filter;
	free(this->fProg);
	free(this->header);
	free(this->net);
	free(this->mask);
	//free(this->fds);
	delete this -> lt;
}

void PacketListener::run()
{
	const u_char* packet;
	char* errstr;
	while (!this->stopFlag)
	{
		packet = pcap_next(this->pcapHandle,this->header);
		if (packet == NULL)
		{
			errstr = pcap_geterr(this->pcapHandle);
			if (strlen(errstr) > 0)
			{
				cout << "error: " << errstr << endl;
				break;
			}
		}
		else
		{
			this -> packetHandler(packet,this->header->len);
		}
	}
}

void PacketListener::stop()
{
	this -> stopFlag = true;
	this -> lt -> join();
}
